package com.qijian.aop;

import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.qijian.common.annotation.DownStreamValidated;
import com.qijian.common.annotation.SourceId;
import com.qijian.common.annotation.SourceType;
import com.qijian.common.interfaceable.DownStreamAble;
import com.qijian.common.utils.mybatis.WhereEntityTool;
import com.qijian.common.utils.reflect.ReflectUtils;
import org.apache.commons.collections4.SetUtils;
import org.apache.logging.log4j.util.Strings;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;


@Aspect
@Component
@Order(1)
public class DownStreamValidatedAop {

    private final String POINT_CUT = "@annotation(com.qijian.common.annotation.DownStreamValidated)";

    @Before(POINT_CUT)
    private void verify(JoinPoint jp) throws Throwable {
        if (!process(jp.getTarget(), jp.getSignature(), jp.getArgs()))
            throw new UnsupportedOperationException("存在下游单据,无法执行操作！");
    }


    private boolean process(Object target, Signature signature, Object[] args) throws Throwable {
        Class<?> clazz = target.getClass();
        Method realMethod = clazz.getDeclaredMethod(signature.getName(), ((MethodSignature) signature).getParameterTypes());
        DownStreamValidated validated = realMethod.getAnnotation(DownStreamValidated.class);
        Class<? extends DownStreamAble> currentClass = validated.value();
        Class<? extends Model<?>>[] downStreamClass;
        //values优先于value
        if (validated.values().length > 0) {
            downStreamClass = validated.values();
        } else {
            downStreamClass = currentClass.newInstance().DownStream();
        }
        //不存在下游，则直接返回
        if (downStreamClass.length == 0)
            return true;
        //TODO 先拿到本体主键id
        Set<Object> ids = new HashSet<>();
        Annotation[][] parameterAnnotations = realMethod.getParameterAnnotations();
        for (int i = 0; i < parameterAnnotations.length; i++) {
            for (Annotation ann : parameterAnnotations[i]) {
                if (SourceId.class.isAssignableFrom(ann.annotationType())) {
                    ids.addAll(exchangeId(args[i], ((SourceId) ann).value()));
                }
            }
        }

        if (ids.size() == 0)
            throw new RuntimeException("无主键参数，无法检测下游");

        for (Object id : ids) {
            for (Class<? extends Model<?>> d : downStreamClass) {
                //找到所有下游的sourceId，填入id
                // 有sourceType就填，没有就不填
                Field sourceId = ReflectUtils.findFieldByAnn(d, SourceId.class);
                Field sourceType = ReflectUtils.findFieldByAnn(d, SourceType.class);
                Assert.notNull(sourceId, "下游单据无法找到关联字段，请使用@SourceId标注");
                Model<?> obj = d.newInstance();
                sourceId.set(obj, id);
                if (sourceType != null && Strings.isNotBlank(validated.sourceType())) {
                    sourceType.set(obj, validated.sourceType());
                }
                long count = obj.selectCount(WhereEntityTool.invoke(obj));
                if (count > 0)
                    return false;
            }
        }
        return true;
    }


    private Set<Object> exchangeId(Object obj, boolean type) {
        try {
            if (type) {
                if (obj instanceof Collection) {
                    Class<?> clazz = ResolvableType.forInstance(obj).getGeneric().resolve();
                    Assert.notNull(clazz, "复杂取值异常，请检查配置");
                    Field idField = ReflectUtils.findObjectIdField(clazz);
                    Assert.notNull(idField, "复杂取值无法找到参数中的主键，请检查配置");
                    return ((Collection<?>) obj).stream().map(i -> {
                        Object o = null;
                        try {
                            o = idField.get(i);
                        } catch (Exception ex) {
                            ex.printStackTrace();
                        }
                        return o;
                    }).collect(Collectors.toSet());
                } else {
                    Field idField = ReflectUtils.findObjectIdField(obj.getClass());
                    Assert.notNull(idField, "复杂取值无法找到参数中的主键，请检查配置");
                    return SetUtils.hashSet(idField.get(obj));
                }
            } else {
                if (obj instanceof Collection) return SetUtils.hashSet(((Collection<?>) obj).toArray());
                else return SetUtils.hashSet(obj);
            }

        } catch (IllegalAccessException ex) {
            ex.printStackTrace();
            return SetUtils.emptySet();
        }
    }

}
